using System.Collections.Generic;
using GameFramework;
using GameFramework.Event;
using GameFramework.Resource;
using UnityEngine;
using UnityGameFramework.Runtime;
using ProcedureOwner = GameFramework.Fsm.IFsm<GameFramework.Procedure.IProcedureManager>;
using ResourceUpdateChangedEventArgs = UnityGameFramework.Runtime.ResourceUpdateChangedEventArgs;
using ResourceUpdateFailureEventArgs = UnityGameFramework.Runtime.ResourceUpdateFailureEventArgs;
using ResourceUpdateStartEventArgs = UnityGameFramework.Runtime.ResourceUpdateStartEventArgs;
using ResourceUpdateSuccessEventArgs = UnityGameFramework.Runtime.ResourceUpdateSuccessEventArgs;

namespace StarForce
{
    public class ProcedureUpdateResource : ProcedureBase
    {
        public override bool UseNativeDialog
        {
            get { return true; }
        }

        /// <summary>
        /// 是否全部更新完成
        /// </summary>
        private bool m_UpdateAllComplete = false;

        /// <summary>
        /// 更新的长度
        /// </summary>
        private int m_UpdateCount = 0;

        /// <summary>
        /// 更新zip长度
        /// </summary>
        private long m_UpdateTotalZipLength = 0;

        /// <summary>
        /// 更新资源成功数量
        /// </summary>
        private int m_UpdateSuccessCount = 0;

        /// <summary>
        /// 更新了哪些数据
        /// </summary>
        private List<UpdateLengthData> m_UpdateLengthData = new List<UpdateLengthData>();

        /// <summary>
        /// 资源更新界面
        /// </summary>
//        private UpdateResourcePage m_UpdateResourcePage = null;

        protected override void OnEnter(ProcedureOwner procedureOwner)
        {
            base.OnEnter(procedureOwner);

            m_UpdateAllComplete = false;
            m_UpdateCount = 0;
            m_UpdateTotalZipLength = 0;
            m_UpdateSuccessCount = 0;
            m_UpdateLengthData.Clear();
//            m_UpdateResourcePage = null;
            GameEntry.Event.Subscribe(ResourceUpdateStartEventArgs.EventId, OnResourceUpdateStart);
            GameEntry.Event.Subscribe(ResourceUpdateChangedEventArgs.EventId, OnResourceUpdateChanged);
            GameEntry.Event.Subscribe(ResourceUpdateSuccessEventArgs.EventId, OnResourceUpdateSuccess);
            GameEntry.Event.Subscribe(ResourceUpdateFailureEventArgs.EventId, OnResourceUpdateFailure);

            GameEntry.Resource.CheckResources(OnCheckResourcesComplete);
        }


        protected override void OnLeave(ProcedureOwner procedureOwner, bool isShutdown)
        {
//            if (m_UpdateResourcePage != null)
//            {
//                Object.Destroy(m_UpdateResourcePage.gameObject);
//                m_UpdateResourcePage = null;
//            }

            GameEntry.Event.Unsubscribe(ResourceUpdateStartEventArgs.EventId, OnResourceUpdateStart);
            GameEntry.Event.Unsubscribe(ResourceUpdateChangedEventArgs.EventId, OnResourceUpdateChanged);
            GameEntry.Event.Unsubscribe(ResourceUpdateSuccessEventArgs.EventId, OnResourceUpdateSuccess);
            GameEntry.Event.Unsubscribe(ResourceUpdateFailureEventArgs.EventId, OnResourceUpdateFailure);

            base.OnLeave(procedureOwner, isShutdown);
        }

        protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);

            if (!m_UpdateAllComplete)
            {
                return;
            }

            //当所有资源更新成功后 进入预加载流程
            ChangeState<ProcedurePreload>(procedureOwner);
        }

        /// <summary>
        /// 检查资源回调
        /// </summary>
        /// <param name="movecount"></param>
        /// <param name="removedcount"></param>
        /// <param name="updatecount"></param>
        /// <param name="updatetotallength"></param>
        /// <param name="updatetotalziplength"></param>
        private void OnCheckResourcesComplete(int movecount, int removedcount, int updatecount, long updatetotallength,
            long updatetotalziplength)
        {
            m_UpdateCount = updatecount;
            m_UpdateTotalZipLength = updatetotalziplength;
            if (updatecount <= 0)
            {
                //当资源不需要更新的时候 直接资源更新完成
                Log.Info("资源不需要更新");
                ProcessUpdateResourcesComplete();
                return;
            }

            //否则开始更新资源
            StartUpdateResources(null);
        }

        /// <summary>
        /// 更新资源完成
        /// </summary>
        private void ProcessUpdateResourcesComplete()
        {
            m_UpdateAllComplete = true;
        }

        /// <summary>
        /// 开始更新资源
        /// </summary>
        /// <param name="userData"></param>
        private void StartUpdateResources(object userData)
        {
            //todo 打开资源加载的界面
//            if (m_UpdateResourcePage == null)
//            {
//                m_UpdateResourcePage = Object.Instantiate(GameEntry.BuiltinData.UpdateResourcePageTemplate);
//            }

            GameEntry.Resource.UpdateResources(OnUpdateResourcesComplete);
            Log.Info("Start update resources...");
        }

        /// <summary>
        /// 检查资源是否更细能完成
        /// </summary>
        /// <param name="resourcegroup"></param>
        /// <param name="result"></param>
        private void OnUpdateResourcesComplete(IResourceGroup resourcegroup, bool result)
        {
            Log.Info("Update resources complete.");
            ProcessUpdateResourcesComplete();
        }

        /// <summary>
        /// 开始更新资源回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnResourceUpdateStart(object sender, GameEventArgs e)
        {
            ResourceUpdateStartEventArgs ne = (ResourceUpdateStartEventArgs) e;

            for (int i = 0; i < m_UpdateLengthData.Count; i++)
            {
                if (m_UpdateLengthData[i].Name == ne.Name)
                {
                    Log.Warning("Update resource '{0}' is invalid.", ne.Name);
                    m_UpdateLengthData[i].Length = 0;
                    RefreshProgress();
                    return;
                }
            }

            //记录下要更新的这个资源的长度数据
            m_UpdateLengthData.Add(new UpdateLengthData(ne.Name));
        }

        /// <summary>
        /// 资源更新中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnResourceUpdateChanged(object sender, GameEventArgs e)
        {
            ResourceUpdateChangedEventArgs ne = (ResourceUpdateChangedEventArgs) e;

            for (int i = 0; i < m_UpdateLengthData.Count; i++)
            {
                if (m_UpdateLengthData[i].Name == ne.Name)
                {
                    m_UpdateLengthData[i].Length = ne.CurrentLength;
                    RefreshProgress();
                    return;
                }
            }

            Log.Warning("Update resource '{0}' is invalid.", ne.Name);
        }

        /// <summary>
        /// 资源更新成功回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnResourceUpdateSuccess(object sender, GameEventArgs e)
        {
            ResourceUpdateSuccessEventArgs ne = (ResourceUpdateSuccessEventArgs) e;
            Log.Info("Update resource '{0}' success.", ne.Name);

            for (int i = 0; i < m_UpdateLengthData.Count; i++)
            {
                if (m_UpdateLengthData[i].Name == ne.Name)
                {
                    m_UpdateLengthData[i].Length = ne.ZipLength;
                    m_UpdateSuccessCount++;
                    RefreshProgress();
                    return;
                }
            }

            Log.Warning("Update resource '{0}' is invalid.", ne.Name);
        }

        /// <summary>
        /// 资源更新失败
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnResourceUpdateFailure(object sender, GameEventArgs e)
        {
            ResourceUpdateFailureEventArgs ne = (ResourceUpdateFailureEventArgs) e;
            if (ne.RetryCount >= ne.TotalRetryCount)
            {
                Log.Error("Update resource '{0}' failure from '{1}' with error message '{2}', retry count '{3}'.",
                    ne.Name, ne.DownloadUri, ne.ErrorMessage, ne.RetryCount.ToString());
                return;
            }
            else
            {
                Log.Info("Update resource '{0}' failure from '{1}' with error message '{2}', retry count '{3}'.",
                    ne.Name, ne.DownloadUri, ne.ErrorMessage, ne.RetryCount.ToString());
            }

            for (int i = 0; i < m_UpdateLengthData.Count; i++)
            {
                if (m_UpdateLengthData[i].Name == ne.Name)
                {
                    m_UpdateLengthData.Remove(m_UpdateLengthData[i]);
                    RefreshProgress();
                    return;
                }
            }

            Log.Warning("Update resource '{0}' is invalid.", ne.Name);
        }


        /// <summary>
        /// 刷新进度
        /// </summary>
        private void RefreshProgress()
        {
            int currentTotalUpdateLength = 0;
            for (int i = 0; i < m_UpdateLengthData.Count; i++)
            {
                currentTotalUpdateLength += m_UpdateLengthData[i].Length;
            }

            //计算更新进度
            float progressTotal = (float) currentTotalUpdateLength / m_UpdateTotalZipLength;

            //获取更新描述文本
            string descriptionText = ""+ GetLengthString(currentTotalUpdateLength)+"/"+GetLengthString(m_UpdateTotalZipLength)+"    "+
                GetLengthString((int) GameEntry.Download.CurrentSpeed)+"/s";

            //todo 更新界面
            GameEntry.BuiltinData.SetProgress(progressTotal, descriptionText);
//            m_UpdateResourcePage.SetProgress(progressTotal, descriptionText);
        }

        /// <summary>
        /// 获取长度字符串
        /// </summary>
        private string GetLengthString(long length)
        {
            if (length < 1024)
            {
                return Utility.Text.Format("{0} Bytes", length.ToString());
            }

            if (length < 1024 * 1024)
            {
                return Utility.Text.Format("{0} KB", (length / 1024f).ToString("F2"));
            }

            if (length < 1024 * 1024 * 1024)
            {
                return Utility.Text.Format("{0} MB", (length / 1024f / 1024f).ToString("F2"));
            }

            return Utility.Text.Format("{0} GB", (length / 1024f / 1024f / 1024f).ToString("F2"));
        }


        /// <summary>
        /// 更新数据基类
        /// </summary>
        private class UpdateLengthData
        {
            private readonly string m_Name;

            public UpdateLengthData(string name)
            {
                m_Name = name;
            }

            public string Name
            {
                get { return m_Name; }
            }

            public int Length { get; set; }
        }
    }
}